package minauth

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"time"

	"gitee.com/haodreams/libs/safe"
	"github.com/gin-gonic/gin"
)

var auth = &Auth{}

// 认证地址
var authURL = "/v1/login/index"

// SetAuthURL 设置认证地址
func SetAuthURL(url string) {
	authURL = url
}

func init() {
	auth.sess.Reset()
	auth.sids.Reset()
}

// Auth ...
type Auth struct {
	sess safe.SafeMap[string, Session]
	sids safe.SafeMap[string, Session]
}

// Hash ...
func Hash(s string) string {
	h := sha1.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

// Register 注册信息
func Register(name, alias, image, clientIP string) (sess *Session) {
	if sess, ok := auth.sess.Get(name); ok && sess != nil { //必须指针地址
		return sess
	}

	sess = new(Session)
	sess.Name = name
	sess.LoginTime = time.Now().Unix()
	sess.LastTime = sess.LoginTime
	sess.Alias = alias
	sess.Image = image
	sess.SID = Hash(fmt.Sprintf("%s%d", name, sess.LoginTime))
	auth.sess.Set(sess.Name, sess)
	auth.sids.Set(sess.SID, sess)
	return
}

// FindByName 根据用户名查找会话
func FindByName(name string) (sess *Session, err error) {
	if sess, ok := auth.sess.Get(name); ok && sess != nil { //必须指针地址
		return sess, nil
	}
	err = errors.New("not this username")
	return
}

// FindBySID 查找
func FindBySID(sid string) (sess *Session, err error) {
	if sess, ok := auth.sids.Get(sid); ok && sess != nil { //必须指针地址
		return sess, nil
	}
	err = errors.New("not this session")
	return
}

// RemoveBySID 移除认证
func RemoveBySID(sid string) {
	if sess, ok := auth.sids.Get(sid); ok && sess != nil { //必须指针地址
		auth.sids.Remove(sid)
		auth.sess.Remove(sess.Name)
	}
}

// RemoveByName 移除认证
func RemoveByName(name string) {
	if sess, ok := auth.sess.Get(name); ok && sess != nil { //必须指针地址
		auth.sess.Remove(name)
		auth.sids.Remove(sess.SID)
	}
}

// Session ...
type Session struct {
	Name      string //名称
	Alias     string //别名
	LoginTime int64  //登陆时间
	LastTime  int64  //最后访问时间
	Image     string //图像
	ClientIP  string //客户端地址
	SID       string //session ID
}

// // CallbackNeedAuth 需要认证回调函数, m gin.H
// func CallbackNeedAuth(c *gin.Context) (ptr interface{}, err error) {
// 	//js 文件不做验证
// 	//有安全风险 ， /table 路径下的js可以不做验证
// 	if strings.HasSuffix(c.Request.RequestURI, ".js") {
// 		return nil, nil
// 	}
// 	if strings.HasSuffix(c.Request.URL.Path, ".js") {
// 		return nil, nil
// 	}
// 	//先验证
// 	uid, err := c.Cookie("uid")
// 	if err != nil {
// 		msg := "需要认证"
// 		if c.Request.Method == "POST" {
// 			return nil, errors.New(msg)
// 		}
// 		c.Redirect(302, authURL+"?lastMsg="+msg+"&lastURL="+c.Request.URL.RequestURI())
// 		return nil, nil
// 	}

// 	sess, err := FindBySID(uid)
// 	if err != nil {
// 		msg := "登录已经过期,请重新登录"
// 		if c.Request.Method == "POST" {
// 			return nil, errors.New(msg)
// 		}
// 		c.Redirect(302, authURL+"?lastMsg="+msg+"&lastURL="+c.Request.URL.RequestURI())
// 		return nil, nil
// 	}

// 	// if m != nil {
// 	// 	m["loginUserName"] = sess.Name
// 	// 	m["loginUserAlias"] = sess.Alias
// 	// 	m["loginUserImage"] = sess.Image
// 	// }
// 	return sess, nil
// }

// CallbackNeedAuth 需要认证回调函数, m gin.H
// 只要 header, cookie, param 参数中任何一个带有uid标识的均可通过验证
func CallbackNeedAuth(c *gin.Context) (ptr interface{}, err error) {
	//js 文件不做验证
	//有安全风险 ， /table 路径下的js可以不做验证
	if strings.HasSuffix(c.Request.RequestURI, ".js") {
		return nil, nil
	}
	if strings.HasSuffix(c.Request.URL.Path, ".js") {
		return nil, nil
	}
	//先验证
	uid := c.Request.Header.Get("Authorization")
	if uid == "" {
		//step1 : 检查cookie中是否带有参数
		uid, err = c.Cookie("uid")
		if err != nil || uid == "" {
			//step2 : 检查参数中是否带有uid
			vals, _ := url.ParseQuery(c.Request.URL.RawQuery)
			uids := vals["uid"]
			if len(uids) > 0 {
				uid = uids[0]
			} else {
				return
			}
		}

		if uid == "" {
			err = errors.New("需要认证")
			return
		}
	}

	sess, err := FindBySID(uid)
	if err != nil {
		err = errors.New("登录已经过期,请重新登录")
		return
	}

	// if m != nil {
	// 	m["loginUserName"] = sess.Name
	// 	m["loginUserAlias"] = sess.Alias
	// 	m["loginUserImage"] = sess.Image
	// }
	ptr = sess
	return
}
